﻿using System;
using System.Collections.Generic;
using System.Data;

namespace Flagwind.Data
{
	public delegate TResult ECResultFunc<TResult>(EntityContainer ec);

	public delegate TResult ECMapFunc<TResult>() where TResult : class;

	public delegate TResult ECMapFunc<T, TResult>(T entity) where T : IEntity where TResult : class;

	public delegate TResult ECMapFunc<T1, T2, TResult>(T1 entity1, T2 entity2) where T1 : IEntity where T2 : IEntity where TResult : class;

	public delegate TResult ECMapFunc<T1, T2, T3, TResult>(T1 entity1, T2 entity2, T3 entity3) where T1 : IEntity where T2 : IEntity where T3 : IEntity where TResult : class;

	/// <summary>
	/// 实体数据容器。
	/// </summary>
	public class EntityContainer
	{
		private Command _Command;
		private IDataProvider _dataProvider;
		private string[] _fieldNames;
		private Type[] _fieldTypes;
		private List<object[]> _values;
		private object[] _currValue;

		/// <summary>
		/// 以 TResult为输入参数，并返回此类型的函数的委托定义
		/// </summary>
		/// <typeparam name="TResult">输入类型</typeparam>
		/// <param name="arg">参数</param>
		/// <returns></returns>
		public delegate TResult Func<TResult>(TResult arg);

		/// <summary>
		/// 默认构造函数
		/// </summary>
		public EntityContainer()
		{
		}

		/// <summary>
		/// 使用Command表达式和数据访问对象实例初始化。如果Command未设置记录数量（等于0），那么查询会先进行一次记录数量查询。
		/// </summary>
		/// <param name="command">Command表达式</param>
		/// <param name="db">数据访问对象实例</param>
		public EntityContainer(Command command, IDataProvider db)
		{
			this.Command = command;
			this.DataBase = db;
		}

		/// <summary>
		/// 使用查询表达式初始化。如果Command未设置记录数量（等于0），那么查询会先进行一次记录数量查询。
		/// </summary>
		/// <param name="command"></param>
		public EntityContainer(Command command)
		{
			this.Command = command;
		}

		/// <summary>
		/// 查询表达式
		/// </summary>
		public Command Command
		{
			get
			{
				return this._Command;
			}
			set
			{
				this._Command = value;
			}
		}

		/// <summary>
		/// 数据访问对象
		/// </summary>
		public IDataProvider DataBase
		{
			get
			{
				if(_dataProvider == null)
				{
					_dataProvider = DataProviderFactory.DefaultProvider;
				}
				return _dataProvider;
			}
			set
			{
				_dataProvider = value;
			}
		}

		private IDataReader ExecuteDataReader(Command command, IDataProvider db)
		{
			string sql = "";
			sql = Command.ToString();
			//处理实体类分页 2010.6.20
			if(Command.PageEnable)
			{
				//处理分页统前的记录数量统计问题 感谢网友 @成都-小兵 发现此问题
				if(Command.RecordCount == 0)
				{
					object oValue = AnonymousAccess.Count(Command, db);
					Command.RecordCount = Utility.ConvertType<int>(oValue);
				}
				switch(db.ProviderType)
				{
					case DataProviderType.Access:
					case DataProviderType.SqlServer:
					case DataProviderType.SqlServerCe:
						if(Command.haveJoinOpt)
						{
							if(Command.PageIndex <= 1) //仅限定记录条数
							{
								sql = "Select Top " + Command.PageSize + " " + sql.Trim().Substring("SELECT ".Length);
							}
							else //必须采用复杂分页方案
							{
								//edit at 2012.10.2 Command.PageWithAllRecordCount
								sql = SqlPager.MakeSQLStringByPage(DataProviderType.SqlServer, sql, "", Command.PageSize, Command.PageIndex, Command.RecordCount);
							}
						}
						else
						{
							//单表查询的情况
							if(Command.PageOrderDesc)
							{
								sql = SqlPager.GetDescPageSQLbyPrimaryKey(Command.PageIndex, Command.PageSize, Command.sql_fields, Command.sql_table, Command.PageField, Command.sql_condition);
							}
							else
							{
								sql = SqlPager.GetAscPageSQLbyPrimaryKey(Command.PageIndex, Command.PageSize, Command.sql_fields, Command.sql_table, Command.PageField, Command.sql_condition);
							}
						}
						break;
					case DataProviderType.Oracle:
						sql = SqlPager.MakeSQLStringByPage(DataProviderType.Oracle, sql, "", Command.PageSize, Command.PageIndex, 999);
						break;
					case DataProviderType.MySql:
						sql = SqlPager.MakeSQLStringByPage(DataProviderType.MySql, sql, "", Command.PageSize, Command.PageIndex, 999);
						break;
					case DataProviderType.PostgreSQL:
						sql = SqlPager.MakeSQLStringByPage(DataProviderType.PostgreSQL, sql, "", Command.PageSize, Command.PageIndex, 999);
						break;
					default:
						throw new Exception("实体类分页错误：不支持此种类型的数据库分页。");
				}
			}
			IDataReader reader = null;
			if(Command.Parameters != null && Command.Parameters.Count > 0)
			{
				int fieldCount = Command.Parameters.Count;
				IDataParameter[] paras = DataUtility.GetParameters(Command.Parameters, db);
				//int index = 0;
				//foreach (string name in Command.Parameters.Keys)
				//{
				//    paras[index] = db.GetParameter(name, Command.Parameters[name]);
				//    index++;
				//}
				reader = db.ExecuteDataReader(sql, CommandType.Text, paras);
			}
			else
			{
				reader = db.ExecuteDataReader(sql);
			}
			return reader;
		}

		/// <summary>
		/// 执行Command查询，并将查询结果缓存。如果未设置记录数量，那么查询会先进行一次记录数量查询。
		/// </summary>
		/// <returns>结果的行数</returns>
		public int Execute()
		{
			IDataReader reader = ExecuteDataReader(this.Command, this.DataBase);
			return Execute(reader);
		}

		/// <summary>
		/// 执行DataReader查询，并将查询结果缓存
		/// </summary>
		/// <param name="reader">数据阅读器</param>
		/// <returns>结果行数</returns>
		public int Execute(IDataReader reader)
		{
			List<object[]> list = new List<object[]>();
			using(reader)
			{
				int fcount = reader.FieldCount;
				_fieldNames = new string[fcount];
				_fieldTypes = new Type[fcount];
				if(reader.Read())
				{
					object[] values = null;
					for(int i = 0; i < fcount; i++)
					{
						_fieldNames[i] = reader.GetName(i);
						_fieldTypes[i] = reader.GetFieldType(i);
					}
					do
					{
						values = new object[fcount];
						reader.GetValues(values);
						list.Add(values);
					} while(reader.Read());
				}
			}
			this._values = list;
			return list.Count;
		}

		/// <summary>
		/// 获取容器数据中的字段名数组
		/// </summary>
		public string[] FieldNames
		{
			get
			{
				return this._fieldNames;
			}
		}

		/// <summary>
		/// 获取容器数据中的每一行的值
		/// </summary>
		/// <returns></returns>
		public IEnumerable<object[]> GetItemValues()
		{
			if(this._values != null && this._fieldNames != null)
			{
				foreach(object[] itemValues in this._values)
				{
					yield return itemValues;
				}
			}
		}

		/// <summary>
		/// 将数据从容器中映射到实体中
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <returns></returns>
		public IEnumerable<T> Map<T>() where T : IEntity, new()
		{
			if(this._values == null)
			{
				int rowsCount = this.Execute();
				if(rowsCount <= 0)
				{
					yield break;
				}
			}
			if(this._values != null && this._fieldNames != null)
			{
				if(this._values.Count == 0)
				{
					yield break;
				}
				Dictionary<string, int> dictNameIndex = new Dictionary<string, int>();
				T entity = new T();
				string tabeName = entity.GetMappingName();
				//查找字段匹配情况
				//entity.GetPropertyNames() 存储的仅仅是查询出来的列名称，由于有连表查询，
				//如果要映射到指定的实体，还得检查当前列对应的表名称
				if(this.Command.sql_fields.Contains("[" + tabeName + "]"))
				{
					//是连表查询
					for(int i = 0; i < this._fieldNames.Length; i++)
					{
						for(int j = 0; j < entity.GetPropertyNames().Length; j++)
						{
							string cmpString = "[" + tabeName + "].[" + entity.GetPropertyNames()[j] + "]";
							if(this.Command.sql_fields.Contains(cmpString))
							{
								dictNameIndex[this._fieldNames[i]] = i;
								break;
							}
						}
					}
				}
				else
				{
					for(int i = 0; i < this._fieldNames.Length; i++)
					{
						for(int j = 0; j < entity.GetPropertyNames().Length; j++)
						{
							if(this._fieldNames[i] == entity.GetPropertyNames()[j])
							{
								dictNameIndex[this._fieldNames[i]] = i;
								break;
							}
						}
					}
				}
				//没有匹配的，提前结束
				if(dictNameIndex.Count == 0)
				{
					yield break;
				}
				int length = entity.GetPropertyValues().Length;
				foreach(object[] itemValues in this._values)
				{
					for(int m = 0; m < length; m++)
					{
						//将容器的值赋值给实体的值元素
						string key = entity.GetPropertyNames()[m];
						if(dictNameIndex.ContainsKey(key))
						{
							entity.GetPropertyValues()[m] = itemValues[dictNameIndex[key]];
						}
					}
					yield return entity;
					//创建一个新实例
					entity = new T();
				}
			}
			else
			{
				throw new Exception("EntityContainer 错误，执行查询没有返回任何行。");
			}
		}

		/// <summary>
		/// 根据Command内部的实体类，将查询结果直接映射到这些实体类实例对象上去
		/// </summary>
		/// <param name="entitys">Command内部的实体类</param>
		/// <returns></returns>
		private IEnumerable<object[]> MapMoreEntity(IEntity[] entitys)
		{
			if(this._values == null)
			{
				int rowsCount = this.Execute();
				if(rowsCount <= 0)
				{
					yield break;
				}
			}
			if(this._values != null && this._fieldNames != null)
			{
				if(this._values.Count == 0)
				{
					yield break;
				}
				TableNameField[] fieldInfo = new TableNameField[this._fieldNames.Length];
				//查找字段匹配情况
				//entity.GetPropertyNames() 存储的仅仅是查询出来的列名称
				for(int i = 0; i < this.Command.selectedFieldInfo.Count; i++)
				{
					TableNameField tnf = this.Command.selectedFieldInfo[i];
					tnf.Index = tnf.Entity.GetPropertyIndex(tnf.Field);
					fieldInfo[i] = tnf;
				}
				foreach(object[] itemValues in this._values)
				{
					for(int m = 0; m < itemValues.Length; m++)
					{
						//将容器的值赋值给实体的值元素
						IEntity entity = fieldInfo[m].Entity;
						int index = fieldInfo[m].Index;
						entity.GetPropertyValues()[index] = itemValues[m];
					}
					yield return itemValues;
				}
			}
			else
			{
				throw new Exception("EntityContainer 错误，执行查询没有返回任何行。");
			}
		}

		private object propertyList(string propertyName, object[] itemValues)
		{
			for(int i = 0; i < _fieldNames.Length; i++)
			{
				if(string.Compare(_fieldNames[i], propertyName, true) == 0)
				{
					return itemValues[i];
				}
			}
			return null;
		}

		/// <summary>
		/// 根据字段名，从当前行获取值
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="fieldName"></param>
		/// <returns></returns>
		public T GetItemValue<T>(string fieldName)
		{
			if(this._currValue != null)
			{
				return Utility.ConvertType<T>(propertyList(fieldName, this._currValue));
			}
			else
			{
				return default(T);
			}
		}

		/// <summary>
		/// 根据字段索引，从当前行获取值
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="fieldIndex"></param>
		/// <returns></returns>
		public T GetItemValue<T>(int fieldIndex)
		{
			if(this._currValue != null)
			{
				return Utility.ConvertType<T>(this._currValue[fieldIndex]);
			}
			else
			{
				return default(T);
			}
		}

		/// <summary>
		/// 采用自定义的映射方式，将数据容器中的数据映射到指定的类中 
		/// </summary>
		/// <typeparam name="TResult">结果类型</typeparam>
		/// <param name="fun">处理数据的方法</param>
		/// <returns></returns>
		public IEnumerable<TResult> Map<TResult>(Func<TResult> fun) where TResult : class, new()
		{
			if(this._values == null)
			{
				this.Execute();
			}
			if(this._values != null && this._fieldNames != null)
			{
				foreach(object[] itemValues in this._values)
				{
					TResult t = new TResult();
					this._currValue = itemValues;
					fun(t);
					yield return t;
				}
			}
			else
			{
				throw new Exception("EntityContainer 错误，执行查询没有返回任何行。");
			}
		}

		/// <summary>
		/// 将实体类的部分字段结果集或者实体类联和查询的字段的结果集映射到一个新的POCO类
		/// </summary>
		/// <typeparam name="T">Command使用的实体类类型</typeparam>
		/// <typeparam name="TResult">要映射的结果类型</typeparam>
		/// <param name="para">Command使用的实体类</param>
		/// <param name="fun">完成映射的用户方法</param>
		/// <returns>映射的用户类型数据列表</returns>
		public IList<TResult> MapToList<T, TResult>(T para, ECMapFunc<T, TResult> fun) where T : IEntity, new() where TResult : class
		{
			this._Command.fieldStack.Clear();
			var result = fun(para);
			int count = this._Command.fieldStack.Count;
			this._Command.Select(new object[count]);
			//已经获取到自定义实体类对象中选择的字段，可以用此Command进行查询了
			List<TResult> resultList = new List<TResult>();
			foreach(T entity in this.Map<T>())
			{
				TResult item = fun(entity);
				resultList.Add(item);
			}
			return resultList;
		}

		/// <summary>
		/// 将实体类的部分字段结果集或者实体类联和查询的字段的结果集映射到一个新的POCO类
		/// </summary>
		/// <typeparam name="TResult">POCO类类型</typeparam>
		/// <param name="fun">包含实体属性访问的POCO类生成函数，注意至少包含2个以上的实体类属性访问</param>
		/// <returns></returns>
		public IList<TResult> MapToList<TResult>(ECMapFunc<TResult> fun) where TResult : class
		{
			this._Command.fieldStack.Clear(); //清除可能的调试信息
			var result = fun();
			int count = this._Command.fieldStack.Count;
			if(count <= 1)
			{
				throw new ArgumentException("要将结果映射的查询的字段太少，如果只需要查询一个字段，请用DataAccess 对象或者其它方式");
			}
			this._Command.Select(new object[count]);
			//已经获取到自定义实体类对象中选择的字段，可以用此Command进行查询了，待完成
			List<TResult> resultList = new List<TResult>();
			foreach(var data in this.MapMoreEntity(this.Command.GetAllUsedEntity()))
			{
				//执行之前，必须先给相应的实体对象赋值，否则报错
				TResult obj = fun();
				resultList.Add(obj);
			}
			return resultList;
		}

		/// <summary>
		/// 将实体类容器转换为对象列表
		/// <example>
		/// <code>
		/// <![CDATA[
		/// Command q=Command.From(entity1)
		///          .Join(entity2).On(entity1.PK,entity2.FK)
		///          .Select(entity1.Field1,entity2.Field2)
		///       .End;
		/// EntityContainer ec=new EntityContainer(q);
		/// var list=ec.ToObjectList( e =>
		///          {
		///             return new {
		///                          Property1=e.GetItemValue<int>(0), 
		///                          Property2=e.GetItemValue<string>(1) 
		///                        };
		///          });
		/// 
		/// foreache(var item in list)
		/// {
		///     Console.WriteLine("Property1={0},Property2={1}",item.Property1,item.Property2);
		/// }
		/// ]]>
		/// </code>
		/// </example>
		/// </summary>
		/// <typeparam name="TResult">结果的列表元素类型</typeparam>
		/// <param name="fun">容器结果委托函数</param>
		/// <returns>对象列表</returns>
		public IList<TResult> ToObjectList<TResult>(ECResultFunc<TResult> fun) where TResult : class
		{
			if(this._values == null)
			{
				this.Execute();
			}
			List<TResult> resultList = new List<TResult>();
			if(this._values != null && this._fieldNames != null)
			{
				foreach(object[] itemValues in this._values)
				{
					this._currValue = itemValues;
					TResult t = fun(this);
					resultList.Add(t);
				}
				return resultList;
			}
			else
			{
				throw new Exception("EntityContainer 错误，执行查询没有返回任何行。");
			}
		}

		/// <summary>
		/// 将容器的结果数据映射到DataTable
		/// </summary>
		/// <param name="tableName">表名称</param>
		/// <returns>DataTable</returns>
		public DataTable MapToDataTable(string tableName)
		{
			if(this._values == null)
			{
				this.Execute();
			}
			if(this._values != null && this._fieldNames != null)
			{
				DataTable dt = new DataTable(tableName);
				for(int i = 0; i < this._fieldNames.Length; i++)
				{
					DataColumn col = new DataColumn(this._fieldNames[i]);
					//下面的方式当数据为空的时候,会报错,感谢网友 @成都-小兵 发现此问题
					//object V = this.Values[0][i];
					//col.DataType = V == null || V == DBNull.Value ? typeof(string) : V.GetType();
					col.DataType = this._fieldTypes[i];
					dt.Columns.Add(col);
				}
				foreach(object[] itemValues in this._values)
				{
					dt.Rows.Add(itemValues);
				}
				return dt;
			}
			else
			{
				throw new Exception("EntityContainer 错误，执行查询没有返回任何行。");
			}
		}
	}
}